package com.tannm.doan.taskmanager.reminder;

import java.util.ArrayList;
import android.database.Cursor;
import java.util.*;

import com.tannm.doan.taskmanager.Prefs;
import com.tannm.doan.taskmanager.database.*;

public class AlarmsManager {
	// types
	public static final int iAction_NONE = 0;
	public static final int iAction_CLEAR = 1;
	public static final int iAction_SNOOZE = 2;
	public static final int iAction_RESET = 3;

	// fields
	private Calendar calNow = Calendar.getInstance();
	private Calendar calActionClear = Calendar.getInstance();
	private Calendar calActionSnooze = Calendar.getInstance();

	// fields
	private Database db = null;
	private Prefs prefs = null;
	private DataRowAlarm rowAlarm = null;
	private DataTable tblAlarms = null;

	// fields
	private ArrayList<AlarmDialogDataItem> vecAlarms = new ArrayList<AlarmDialogDataItem>();

	// methods
	public AlarmsManager(Database db, Prefs prefs) {
		this.db = db;
		this.prefs = prefs;
		rowAlarm = new DataRowAlarm(db);
		tblAlarms = new DataTable(rowAlarm);
	}

	public void clear() {
		vecAlarms.clear();
	}

	public void putAlarmToProcess(AlarmDialogDataItem alarm, int iAction) {
		alarm.setAction(iAction);
		vecAlarms.add(alarm);
	}

	public void processAll() {
		for (int i = 0; i < vecAlarms.size(); i++) {
			final Database.Result result = processAlarm(vecAlarms.get(i));
			if (result != Database.Result.Success) {
				// utils.MsgResStr(Database.GetErrDesc(result),
				// Utils.MsgType.Error);
				// break;
			}
		}
	}

	private Database.Result processAlarm(AlarmDialogDataItem alarm) {
		final int iType = alarm.getOrderFilter();
		final long lRefID = alarm.getID();
		final int iAction = alarm.getAction();

		// update alarms table
		final Database.Result result = updateAlarmDataRow(iType, lRefID,
				iAction);
		return result;
	}

	private void setAlarmAction(int iType, long lRefID, int iAction) {
		long lCurrTimeNow = System.currentTimeMillis();

		rowAlarm.lType = iType;
		rowAlarm.lRefID = lRefID;

		rowAlarm.lActionClear = 0;
		rowAlarm.lActionSnooze = 0;

		if (iAction == iAction_CLEAR) {
			rowAlarm.lActionClear = lCurrTimeNow;
			rowAlarm.lSnoozeCount = 0;
		}

		if (iAction == iAction_SNOOZE) {
			rowAlarm.lActionSnooze = lCurrTimeNow;
			rowAlarm.lSnoozeCount = rowAlarm.lSnoozeCount + 1;

			// clear alarm if snooze count has max value
			if (rowAlarm.lSnoozeCount > prefs.iSnoozeCount)
				setAlarmAction(iType, lRefID, iAction_CLEAR);
		}

		if (iAction == iAction_RESET) {
			rowAlarm.lActionClear = 0;
			rowAlarm.lActionSnooze = 0;
			rowAlarm.lSnoozeCount = 0;
		}
	}

	private Database.Result updateAlarmDataRow(int iType, long lRefID,
			int iAction) {
		Database.Result result = Database.Result.errUnknown;
		final Cursor cr = tblAlarms.locateAlarmDataRow(iType, lRefID);
		if (cr == null) {
			result = Database.Result.errCantGetData;
		} else {
			// update table
			if (cr.getCount() > 0) {
				// update existing row
				if (rowAlarm.getValuesFromCursor(cr)) {
					try {
						rowAlarm.getValuesFromDataRow();
						setAlarmAction(iType, lRefID, iAction);
						cr.close();
						result = dataRowUpdate(iType, lRefID);
					} catch (Exception e) {
						return Database.Result.errCantGetValuesFromDataRow;
					}
				} else {
					result = Database.Result.errCantGetDataFromTable;
				}
			} else {
				// insert new row
				rowAlarm.lSnoozeCount = 1;
				setAlarmAction(iType, lRefID, iAction);
				result = dataRowInsert();
			}
		}
		return result;
	}

	private Database.Result dataRowInsert() {
		Database.Result result = Database.Result.errUnknown;
		// get data to ContentValues
		rowAlarm.setValuesForDataRow();
		// insert data row
		long lRowId = db.getSQLiteDb().insert(tblAlarms.getTableName(), null,
				rowAlarm.getContentValues());
		if (lRowId != -1)
			result = Database.Result.Success;
		return result;
	}

	private Database.Result dataRowUpdate(int iType, long lRefID) {
		Database.Result result = Database.Result.errUnknown;
		// get data to ContentValues
		rowAlarm.setValuesForDataRow();
		// update data row
		final String sWhere = String.format("Type = %d and RefID = %d", iType,
				lRefID);
		long lRowsUpdated = db.getSQLiteDb().update(tblAlarms.getTableName(),
				rowAlarm.getContentValues(), sWhere, null);
		if (lRowsUpdated == 1)
			result = Database.Result.Success;
		return result;
	}

	public static boolean deleteAlarm(Database db, Prefs prefs,
			DataTable tblSource, long lRefID) {
		if (AlarmsManager.alarmToProcess(tblSource)) {
			AlarmsManager am = new AlarmsManager(db, prefs);
			return (am.dataRowDelete(tblSource, lRefID) == Database.Result.Success);
		}
		return false;
	}

	public static boolean resetAlarm(Database db, Prefs prefs,
			DataTable tblSource, long lRefID) {
		if (AlarmsManager.alarmToProcess(tblSource)) {
			AlarmsManager am = new AlarmsManager(db, prefs);
			return (am.dataRowReset(tblSource, lRefID) == Database.Result.Success);
		}
		return false;
	}

	private static boolean alarmToProcess(DataTable tblSource) {
		// get source data/table type
		int iType = -1;
		if (tblSource.getTableName().equals(Database.sTableNameAppointments))
			iType = AlarmDataViewItem.iOrderAppts;
		if (tblSource.getTableName().equals(Database.sTableNameTasks))
			iType = AlarmDataViewItem.iOrderTasks;
		// process alarm
		if (iType != -1)
			return true;
		return false;
	}

	public Database.Result dataRowReset(DataTable tblSource, long lRefID) {
		// get source data/table type
		int iType = -1;
		if (tblSource.getTableName().equals(Database.sTableNameAppointments))
			iType = AlarmDataViewItem.iOrderAppts;
		if (tblSource.getTableName().equals(Database.sTableNameTasks))
			iType = AlarmDataViewItem.iOrderTasks;
		// delete alarm
		Database.Result result = Database.Result.errUnknown;
		if (iType != -1) {
			// delete data row
			result = updateAlarmDataRow(iType, lRefID, iAction_RESET);
		}
		return result;
	}

	public Database.Result dataRowDelete(DataTable tblSource, long lRefID) {
		// get source data/table type
		int iType = -1;
		if (tblSource.getTableName().equals(Database.sTableNameAppointments))
			iType = AlarmDataViewItem.iOrderAppts;
		if (tblSource.getTableName().equals(Database.sTableNameTasks))
			iType = AlarmDataViewItem.iOrderTasks;
		// delete alarm
		Database.Result result = Database.Result.errUnknown;
		if (iType != -1) {
			// delete data row
			result = dataRowDelete(iType, lRefID);
		}
		return result;
	}

	private Database.Result dataRowDelete(int iType, long lRefID) {
		Database.Result result = Database.Result.errUnknown;
		// delete data row
		final String sWhere = String.format("Type = %d and RefID = %d", iType,
				lRefID);
		long lRowsUpdated = db.getSQLiteDb().delete(tblAlarms.getTableName(),
				sWhere, null);
		if (lRowsUpdated == 1)
			result = Database.Result.Success;
		return result;
	}

	private boolean isAlarmTodayCleared() {
		if (rowAlarm.lActionClear > 0) {
			calNow.setTimeInMillis(System.currentTimeMillis());
			calActionClear.setTimeInMillis(rowAlarm.lActionClear);

			if (calActionClear.get(Calendar.YEAR) == calNow.get(Calendar.YEAR))
				if (calActionClear.get(Calendar.MONTH) == calNow
						.get(Calendar.MONTH))
					if (calActionClear.get(Calendar.DAY_OF_MONTH) == calNow
							.get(Calendar.DAY_OF_MONTH))
						return true;
		}
		return false;
	}

	private boolean isAlarmCleared() {
		return (rowAlarm.lActionClear > 0);
	}

	private boolean isAlarmSnoozed() {
		return (rowAlarm.lActionSnooze > 0);
	}

	private boolean isAlarmSnoozeOverdue() {
		if (rowAlarm.lActionSnooze > 0) {
			calNow.setTimeInMillis(System.currentTimeMillis());
			calActionSnooze.setTimeInMillis(rowAlarm.lActionSnooze);

			// minus one, because we test AFTER
			final int iSnoozeMinutes = prefs.iSnoozeMinutesOverdue - 1;

			calActionSnooze.add(Calendar.MINUTE, iSnoozeMinutes);
			if (calNow.after(calActionSnooze))
				return true;
		}
		return false;
	}

	// input: iType: appt/task, lRefID of appointment/task to check
	public boolean canShowAlarmToday(int iType, long lRefID,
			final boolean bIsRepeat) {
		final Cursor cr = tblAlarms.locateAlarmDataRow(iType, lRefID);
		if ((cr != null) && (cr.getCount() > 0)) {
			if (rowAlarm.getValuesFromCursor(cr)) {
				rowAlarm.getValuesFromDataRow();
				cr.close();

				// check alarm show condition
				if (bIsRepeat) {
					if (isAlarmTodayCleared())
						return false;
				} else {
					if (isAlarmCleared())
						return false;
				}

				// check alarm snooze condition
				if (isAlarmSnoozed())
					if (!isAlarmSnoozeOverdue())
						return false;

			}
		}
		return true;
	}

}
